//===--- OptimizationRemark.cpp - Optimization diagnostics ------*- C++ -*-===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
//
/// \file
/// This file defines the remark type and the emitter class that passes can use
/// to emit optimization diagnostics.
//
//===----------------------------------------------------------------------===//

#include "polarphp/pil/lang/OptimizationRemark.h"
#include "polarphp/ast/DiagnosticEngine.h"
#include "polarphp/ast/DiagnosticsPIL.h"
#include "polarphp/demangling/Demangler.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/Support/YAMLTraits.h"
#include "llvm/Support/raw_ostream.h"

using namespace polar;
using namespace polar::optremark;

Argument::Argument(StringRef Key, int N) : Key(Key), Val(llvm::itostr(N)) {}

Argument::Argument(StringRef Key, long N) : Key(Key), Val(llvm::itostr(N)) {}

Argument::Argument(StringRef Key, long long N)
   : Key(Key), Val(llvm::itostr(N)) {}

Argument::Argument(StringRef Key, unsigned N)
   : Key(Key), Val(llvm::utostr(N)) {}

Argument::Argument(StringRef Key, unsigned long N)
   : Key(Key), Val(llvm::utostr(N)) {}

Argument::Argument(StringRef Key, unsigned long long N)
   : Key(Key), Val(llvm::utostr(N)) {}

Argument::Argument(StringRef Key, PILFunction *F)
   : Key(Key) {
   auto DO = demangling::DemangleOptions::SimplifiedUIDemangleOptions();
   // Enable module names so that we have a way of filtering out
   // stdlib-related remarks.
   DO.DisplayModuleNames = true;

   Val = (Twine("\"") + demangling::demangleSymbolAsString(F->getName(), DO) +
          "\"")
      .str();

   if (F->hasLocation())
      Loc = F->getLocation().getSourceLoc();
}

Argument::Argument(StringRef Key, PILType Ty) : Key(Key) {
   llvm::raw_string_ostream OS(Val);
   Ty.print(OS);
}

Argument::Argument(StringRef Key, CanType Ty) : Key(Key) {
   llvm::raw_string_ostream OS(Val);
   Ty.print(OS);
}

template <typename DerivedT> std::string Remark<DerivedT>::getMsg() const {
   std::string Str;
   llvm::raw_string_ostream OS(Str);
   for (const Argument &Arg : Args)
      OS << Arg.Val;
   return OS.str();
}

template <typename DerivedT> std::string Remark<DerivedT>::getDebugMsg() const {
   std::string Str;
   llvm::raw_string_ostream OS(Str);

   if (IndentDebugWidth)
      OS << std::string(" ", IndentDebugWidth);

   for (const Argument &Arg : Args)
      OS << Arg.Val;

   OS << "\n";
   return OS.str();
}

Emitter::Emitter(StringRef PassName, PILModule &M)
   : Module(M), PassName(PassName),
     PassedEnabled(
        M.getAstContext().LangOpts.OptimizationRemarkPassedPattern &&
        M.getAstContext().LangOpts.OptimizationRemarkPassedPattern->match(
           PassName)),
     MissedEnabled(
        M.getAstContext().LangOpts.OptimizationRemarkMissedPattern &&
        M.getAstContext().LangOpts.OptimizationRemarkMissedPattern->match(
           PassName)) {}

template <typename RemarkT, typename... ArgTypes>
static void emitRemark(PILModule &Module, const Remark<RemarkT> &R,
                       Diag<ArgTypes...> ID, bool DiagEnabled) {
   if (R.getLocation().isInvalid())
      return;
   if (auto *Out = Module.getOptRecordStream())
      // YAMLTraits takes a non-const reference even when outputting.
      *Out << const_cast<Remark<RemarkT> &>(R);
   if (DiagEnabled)
      Module.getAstContext().Diags.diagnose(R.getLocation(), ID, R.getMsg());
}

void Emitter::emit(const RemarkPassed &R) {
   emitRemark(Module, R, diag::opt_remark_passed, isEnabled<RemarkPassed>());
}

void Emitter::emit(const RemarkMissed &R) {
   emitRemark(Module, R, diag::opt_remark_missed, isEnabled<RemarkMissed>());
}

void Emitter::emitDebug(const RemarkPassed &R) {
   llvm::dbgs() << R.getDebugMsg();
}

void Emitter::emitDebug(const RemarkMissed &R) {
   llvm::dbgs() << R.getDebugMsg();
}

namespace llvm {
namespace yaml {

template <typename KindT> struct MappingTraits<Remark<KindT>> {
static void mapping(llvm::yaml::IO &io, Remark<KindT> &R) {
   assert(io.outputting() && "input not implemented");

   if (io.mapTag("!Passed", std::is_same<KindT, RemarkPassed>::value))
      ;
   else if (io.mapTag("!Missed", std::is_same<KindT, RemarkMissed>::value))
      ;
   else
      llvm_unreachable("Unknown remark type");

   // The attributes are read-only for now since we're only support outputting
   // them.
   StringRef PassName = R.getPassName();
   io.mapRequired("Pass", PassName);
   std::string Id = (Twine("sil.") + R.getIdentifier()).str();
   io.mapRequired("Name", Id);

   SourceLoc Loc = R.getLocation();
   if (!io.outputting() || Loc.isValid())
      io.mapOptional("DebugLoc", Loc);

   std::string FN = demangling::demangleSymbolAsString(
      R.getFunction()->getName(),
      demangling::DemangleOptions::SimplifiedUIDemangleOptions());
   io.mapRequired("Function", FN);
   io.mapOptional("Args", R.getArgs());
}
};

template <> struct MappingTraits<SourceLoc> {
   static void mapping(IO &io, SourceLoc &Loc) {
      assert(io.outputting() && "input not yet implemented");

      SourceManager *SM = static_cast<SourceManager *>(io.getContext());
      StringRef File = SM->getDisplayNameForLoc(Loc);
      unsigned Line, Col;
      std::tie(Line, Col) = SM->getLineAndColumn(Loc);

      io.mapRequired("File", File);
      io.mapRequired("Line", Line);
      io.mapRequired("Column", Col);
   }
};

// Implement this as a mapping for now to get proper quotation for the value.
template <> struct MappingTraits<optremark::Argument> {
   static void mapping(IO &io, optremark::Argument &A) {
      assert(io.outputting() && "input not yet implemented");
      io.mapRequired(A.Key.data(), A.Val);
      if (A.Loc.isValid())
         io.mapOptional("DebugLoc", A.Loc);
   }
};

} // end namespace yaml
} // end namespace llvm

LLVM_YAML_IS_SEQUENCE_VECTOR(optremark::Argument)
